package labox.innovation.db;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javolution.util.FastList;
import labox.innovation.config.Config;
import labox.innovation.db.core.HeapByteBuffer;
import labox.innovation.db.core.SqlData;
import labox.innovation.db.manager.DBConnetionStateManager;
import labox.innovation.db.proxy.ConnectionProxy;
import labox.innovation.db.proxy.PreparedStatementProxy;
import labox.innovation.db.util.DbUtil;
import labox.innovation.db.util.SqlDataIdFactory;
import labox.innovation.gameserver.datatables.SkillTable;
import labox.innovation.gameserver.model.L2Effect;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.FuzeSkillCoolDown.TimeStamp;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PlayerSkills {
	public static final Logger _log = LoggerFactory.getLogger(PlayerSkills.class.getName());

	/**
	 * ���漼��Ч��
	 */
	private static final String SAVE_SKILLS_EFFECT = "REPLACE INTO character_skills_save (charId,details,restore_type) VALUES (?,?,?)";
	/**
	 * ���ؼ���Ч������ȴ״̬
	 */
	private static final String LOAD_SKILLS_EFFECT = "SELECT charId,details from character_skills_save where charId=? and restore_type=?";
	/**
	 * �����ɫ����
	 */
	private static final String SAVE_SKILLS = "REPLACE INTO character_skills (charId,details,sum_skill_level) VALUES (?,?,?)";
	/**
	 * ���ؽ�ɫ����
	 */
	private static final String LOAD_SKILLS = "SELECT details,sum_skill_level FROM character_skills WHERE charId=?";

	/**
	 * ɾ���ɫ����Ч��
	 */
	public static final String PHYSICAL_DELETE_SKILLSAVE_BY_OWNERID = "delete from character_skills_save where charId=?";

	/**
	 * ɾ���ɫ����
	 */
	public static final String PHYSICAL_DELETE_SKILL_BY_OWNERID = "delete from character_skills where charId=?";

	/**
	 * ���漼��Ч��
	 * @param storeEffects
	 * @param player
	 * @throws SQLException
	 */
	public static void storeEffect(boolean storeEffects, FuzePcInstance player) {
		if (!Config.STORE_SKILL_COOLTIME)
			return;
		int _bufpostion;
		Object params[] = null;
		ConnectionProxy con = null;
		SqlData data = null;
		PreparedStatementProxy ptmt = null;
		try {
			int buff_index = 0;
			final List<Integer> storedSkills = new FastList<Integer>();
			// Store all effect data along with calulated remaining
			// reuse delays for matching skills. 'restore_type'= 0.
			HeapByteBuffer _buf = HeapByteBuffer.newInstance();
			_buf.clear();
			// skill_id1,skill_level2,effect_count3,effect_cur_time4,reuse_delay5,systime6,restore_type7,buff_index8)
			if (storeEffects) {
				int _length = player.getAllEffects().length;
				_buf.setPosition(1);
				for (L2Effect effect : player.getAllEffects()) {
					if (effect == null)
						continue;
					switch (effect.getEffectType()) {
					case HEAL_OVER_TIME:
					case COMBAT_POINT_HEAL_OVER_TIME:
						// Fix me.
					case HIDE:
						_length = _length - 1;
						continue;
					}

					L2Skill skill = effect.getSkill();
					int skillId = skill.getId();
					if (storedSkills.contains(skillId)) {
						_length = _length - 1;
						continue;
					}
					storedSkills.add(skillId);
					if (effect != null && !effect.isHerbEffect() && effect.getInUse()) {
						_buf.writeInt(skillId);// 1
						_buf.writeInt(skill.getLevel());// 2
						_buf.writeInt(effect.getCount());// 3
						_buf.writeInt(effect.getTime());// 4
						TimeStamp t = null;
						if (player.getSkillCoolList().getSkillTimeStamps().containsKey(skillId)) {
							t = player.getSkillCoolList().getSkillTimeStamps().get(skillId);
							_buf.writeInt(t.getItemId());// 5
							_buf.writeInt(t.getSkill());// 6
							_buf.writeLong(t.hasNotPassed() ? t.getReuse() : 0);// 7
							_buf.writeLong(t.hasNotPassed() ? t.getRemaining() : 0);// 8
							if (player.getSkillCoolList().isSkillCategoryCoolDown(skill)) {
								_buf.writeInt(player.getSkillCoolList().getSkillCatCoolTime(skill.getSkillCategory()));// 9
							} else {
								_buf.writeInt(0);// 9
							}
						} else {
							t = player.getSkillCoolList().getSkillTimeStamps().get(skillId);
							if (t != null) {
								_buf.writeInt(t.getItemId());// 5
								_buf.writeInt(t.getSkill());// 6
							} else {
								_buf.writeInt(0);// 5
								_buf.writeInt(0);// 6
							}
							_buf.writeLong(0);// 7
							_buf.writeLong(0);// 8
							if (player.getSkillCoolList().isSkillCategoryCoolDown(skill)) {
								_buf.writeInt(player.getSkillCoolList().getSkillCatCoolTime(skill.getSkillCategory()));// 9
							} else {
								_buf.writeInt(0);// 9
							}
						}
						// _0buf.writeInt(0);// 7
						_buf.writeInt(++buff_index);// 10
					} else {
						_length = _length - 1;
					}
				}
				_bufpostion = _buf.getPosition();
				_buf.setPosition(0);
				_buf.writeByte(_length);
				_buf.setPosition(_bufpostion);
				_buf.flip();
				// _buf.write(_0buf.getBytes(), 0, _0buf.limit());

				if (!DBConnetionStateManager.getDbState()) {
					params = new Object[] { player.getObjectId(), _buf, 0 };
					data = new SqlData(SqlDataIdFactory.createSqlId("character_skills_save_0", player.getObjectId()), SAVE_SKILLS_EFFECT, params);
					DBConnetionStateManager.addDbTask(data);
				} else {
					con = ConnectionProxy.newInstance();
					if (con != null)
						ptmt = con.prepareStatement(SAVE_SKILLS_EFFECT);
					if (DbUtil.isDbNormal(DBConnetionStateManager.getDbState(), con, ptmt)) {
						ptmt.setLong(1, player.getObjectId());
						ptmt.setBinaryStream(2, _buf, _buf.limit());
						ptmt.setInt(3, 0);// ��ɫ�ļ���Ч��״̬
						int rs = ptmt.executeUpdate();
						if (rs != PreparedStatementProxy.NETWORKDISCONNECTION) {
							HeapByteBuffer.recycle(_buf);
						} else {
							params = new Object[] { player.getObjectId(), _buf, 0 };
							data = new SqlData(SqlDataIdFactory.createSqlId("character_skills_save_0", player.getObjectId()), SAVE_SKILLS_EFFECT, params);
							DBConnetionStateManager.addDbTask(data);
						}
					} else {
						params = new Object[] { player.getObjectId(), _buf, 0 };
						data = new SqlData(SqlDataIdFactory.createSqlId("character_skills_save_0", player.getObjectId()), SAVE_SKILLS_EFFECT, params);
						DBConnetionStateManager.addDbTask(data);
					}
				}
			}
			// Store the reuse delays of remaining skills which
			// lost effect but still under reuse delay. 'restore_type' 1.
			HeapByteBuffer _buf1 = HeapByteBuffer.newInstance();
			_buf1.clear();
			_buf1.setPosition(1);
			int _size = player.getSkillCoolList().getSkillTimeStamps().values().size();
			for (TimeStamp t : player.getSkillCoolList().getSkillTimeStamps().values()) {
				if (t.hasNotPassed()) {
					int skillId = t.getSkill();
					if (storedSkills.contains(skillId)) {
						_size = _size - 1;
						continue;
					}
					storedSkills.add(skillId);
					// statement.setLong(1, player.getObjectId());
					_buf1.writeInt(skillId);// 1
					_buf1.writeInt(-1);// 2
					_buf1.writeInt(-1);// 3
					_buf1.writeInt(-1);// 4
					_buf1.writeInt(t.getItemId());// 5
					_buf1.writeInt(t.getSkill());// 6
					_buf1.writeLong(t.getReuse());// 7
					_buf1.writeLong(t.getRemaining());// 8

					if (player.getSkillCoolList().isSkillCategoryCoolDown(skillId)) {
						_buf1.writeInt(player.getSkillCoolList().getSkillCatCoolTime(skillId));// 9
					} else {
						_buf1.writeInt(0);// 9
					}
					// _1buf.writeInt(1);
					_buf1.writeInt(++buff_index);// 10
				} else {
					_size = _size - 1;
				}
			}
			_bufpostion = _buf1.getPosition();
			_buf1.setPosition(0);
			_buf1.writeByte(_size);
			_buf1.setPosition(_bufpostion);
			_buf1.flip();

			if (!DBConnetionStateManager.getDbState()) {
				params = new Object[] { player.getObjectId(), _buf1, 1 };
				data = new SqlData(SqlDataIdFactory.createSqlId("character_skills_save_1", player.getObjectId()), SAVE_SKILLS_EFFECT, params);
				DBConnetionStateManager.addDbTask(data);
			} else {
				if (con == null)
					con = ConnectionProxy.newInstance();
				if (ptmt == null && con != null)
					ptmt = con.prepareStatement(SAVE_SKILLS_EFFECT);

				if (DbUtil.isDbNormal(DBConnetionStateManager.getDbState(), con, ptmt)) {
					ptmt.setLong(1, player.getObjectId());
					ptmt.setBinaryStream(2, _buf1, _buf1.limit());
					ptmt.setInt(3, 1);// ������ȴ״̬
					int rs = ptmt.executeUpdate();
					if (rs != PreparedStatementProxy.NETWORKDISCONNECTION) {
						HeapByteBuffer.recycle(_buf1);
					} else {
						params = new Object[] { player.getObjectId(), _buf1, 1 };
						data = new SqlData(SqlDataIdFactory.createSqlId("character_skills_save_1", player.getObjectId()), SAVE_SKILLS_EFFECT, params);
						DBConnetionStateManager.addDbTask(data);
					}
				} else {
					params = new Object[] { player.getObjectId(), _buf1, 1 };
					data = new SqlData(SqlDataIdFactory.createSqlId("character_skills_save_1", player.getObjectId()), SAVE_SKILLS_EFFECT, params);
					DBConnetionStateManager.addDbTask(data);
				}
			}

		} catch (SQLException e) {
			_log.error("save skill Effects  to db error", Config.SERVER_ID, e);
			e.printStackTrace();
		} finally {
			if (ptmt != null)
				ptmt.close();
			if (con != null)
				con.close();
		}
	}

	/**
	 * ���ؽ�ɫ����Ч�� Retrieve from the database all skill effects of this
	 * L2PcInstance and add them to the player.<BR>
	 * <BR>
	 */
	public static void restoreEffects(FuzePcInstance player) {
		ConnectionProxy con = null;
		PreparedStatementProxy ptmt = null;
		ResultSet res = null;
		HeapByteBuffer _buf = HeapByteBuffer.newInstance();
		try {
			if (!DBConnetionStateManager.getDbState())
				return;
			con = ConnectionProxy.newInstance();
			if (con != null)
				ptmt = con.prepareStatement(LOAD_SKILLS_EFFECT);
			if (!DbUtil.isDbNormal(DBConnetionStateManager.getDbState(), con, ptmt))
				return;
			ptmt.setLong(1, player.getObjectId());
			ptmt.setInt(2, 0);
			/**
			 * Restore Type 0 These skill were still in effect on the character
			 * upon logout. Some of which were self casted and might still have
			 * had a long reuse delay which also is restored.
			 */
			res = ptmt.executeQuery();
			if (res == null)
				return;
			int skillId;// 1
			int skillLvl;// 2
			int effectCount;// 3
			int effectCurTime;// 4
			int itemId;// 5
			int _skillId;// 6
			int skillCategory;// 7
			long reuseDelay;// 8
			long systime;// 9
			byte[] bytes;
			while (res.next()) {
				_buf.clear();
				bytes = res.getBytes("details");
				_buf.write(bytes, 0, bytes.length);
				_buf.flip();
				int size = _buf.readByte();
				for (int i = 0; i < size; i++) {
					skillId = _buf.readInt();// 1
					skillLvl = _buf.readInt();// 2
					effectCount = _buf.readInt();// 3
					effectCurTime = _buf.readInt();// 4
					itemId = _buf.readInt();// 5
					_skillId = _buf.readInt();// 6
					reuseDelay = _buf.readLong();// 7
					systime = _buf.readLong();// 8
					skillCategory = _buf.readInt();// 9
					// _buf.readInt();
					_buf.readInt();// 10
					int remainingTime = (int) (systime - System.currentTimeMillis());
					// Just incase the admin minipulated this table incorrectly
					// :x
					if (skillId == -1 || effectCount == -1 || effectCurTime == -1 || reuseDelay < 0) {

						continue;
					}
					// System.out.println("skillId:" + skillId + " skillLvl:" +
					// skillLvl);

					L2Skill skill = SkillTable.getInstance().getInfo(skillId, skillLvl);
					if (skill != null) {
						L2Effect[] effects = skill.getEffects(player, player);
						if (remainingTime > 10) {
							player.disableSkill(skillId, remainingTime);
							// player.addTimeStamp(new TimeStamp(skillId, (int)
							// reuseDelay, (long) systime));
							if (_skillId != 0 || itemId != 0)
								player.addTimeStamp(_skillId, itemId, (int) reuseDelay, skillCategory);
						}
						for (L2Effect effect : effects) {
							effect.setCount(effectCount);
							effect.setFirstTime(effectCurTime);
						}
					}
				}
			}
			_buf.clear();
			/**
			 * Restore Type 1 The remaning skills lost effect upon logout but
			 * were still under a high reuse delay.
			 */
			ptmt.setLong(1, player.getObjectId());
			ptmt.setInt(2, 1);
			res = ptmt.executeQuery();
			if (res == null)
				return;
			while (res.next()) {
				bytes = res.getBytes("details");
				_buf.write(bytes, 0, bytes.length);
				_buf.flip();
				int size = _buf.readByte();
				for (int i = 0; i < size; i++) {
					skillId = _buf.readInt();// 1
					skillLvl = _buf.readInt();// 2
					effectCount = _buf.readInt();// 3
					effectCurTime = _buf.readInt();// 4
					itemId = _buf.readInt();// 5
					_skillId = _buf.readInt();// 6
					reuseDelay = _buf.readLong();// 7
					systime = _buf.readLong();// 8
					skillCategory = _buf.readInt();// 9
					// _buf.readInt();//
					_buf.readInt();// 10
					// int remainingTime = (int) (systime -
					// System.currentTimeMillis());
					if (systime < 10)
						continue;
					player.disableSkill(skillId, (int) systime);
					player.addTimeStamp(_skillId, itemId, (int) reuseDelay, skillCategory);
				}
			}
		} catch (Exception e) {
			_log.error("Error PlayerSkills restoreEffects", Config.SERVER_ID, e);
			e.printStackTrace();
		} finally {
			HeapByteBuffer.recycle(_buf);
			try {
				if (res != null)
					res.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			if (ptmt != null)
				ptmt.close();
			if (con != null)
				con.close();
		}
	}

	/**
	 * �����ɫ����
	 * @param player
	 * @throws SQLException
	 */
	public static void storeSkill(FuzePcInstance player) {
		Object params[] = null;
		ConnectionProxy con = null;
		SqlData data = null;
		PreparedStatementProxy ptmt = null;
		HeapByteBuffer _buf = HeapByteBuffer.newInstance();
		_buf.clear();
		_buf.setPosition(1);
		int _size = player.getAllSkills().length;
		int _sum_skill_level = 0;
		for (L2Skill l2Skill : player.getAllSkills()) {
			if (l2Skill == null) {
				_size = _size - 1;
				continue;
			}
			_buf.writeInt(l2Skill.getId());
			_buf.writeInt(l2Skill.getLevel());
			_sum_skill_level += l2Skill.getLevel();
			// _log.info("l2Skill.getLevel()"+l2Skill.getLevel());
		}
		int _bufposition = _buf.getPosition();
		_buf.setPosition(0);
		_buf.writeByte(_size);
		_buf.setPosition(_bufposition);
		_buf.flip();

		try {
			if (!DBConnetionStateManager.getDbState()) {
				params = new Object[] { player.getObjectId(), _buf, _sum_skill_level };
				data = new SqlData(SqlDataIdFactory.createSqlId("character_skills", player.getObjectId()), SAVE_SKILLS, params);
				DBConnetionStateManager.addDbTask(data);
				return;
			}
			con = ConnectionProxy.newInstance();
			if (con != null)
				ptmt = con.prepareStatement(SAVE_SKILLS);
			if (DbUtil.isDbNormal(DBConnetionStateManager.getDbState(), con, ptmt)) {
				ptmt.setLong(1, player.getObjectId());
				ptmt.setBinaryStream(2, _buf, _buf.limit());
				ptmt.setInt(3, _sum_skill_level);
				int rs = ptmt.executeUpdate();
				if (rs != PreparedStatementProxy.NETWORKDISCONNECTION) {
					HeapByteBuffer.recycle(_buf);
				} else {
					params = new Object[] { player.getObjectId(), _buf, _sum_skill_level };
					data = new SqlData(SqlDataIdFactory.createSqlId("character_skills", player.getObjectId()), SAVE_SKILLS, params);
					DBConnetionStateManager.addDbTask(data);
				}
			} else {
				params = new Object[] { player.getObjectId(), _buf, _sum_skill_level };
				data = new SqlData(SqlDataIdFactory.createSqlId("character_skills", player.getObjectId()), SAVE_SKILLS, params);
				DBConnetionStateManager.addDbTask(data);
			}
		} catch (SQLException e) {
			_log.error("save character skills", Config.SERVER_ID, e);
			e.printStackTrace();
		} finally {
			if (ptmt != null)
				ptmt.close();
			if (con != null)
				con.close();
		}
	}

	/**
	 * ���ؽ�ɫ����
	 * @param player
	 */
	public static void restoreSkill(FuzePcInstance player) {
		ConnectionProxy con = null;
		PreparedStatementProxy ptmt = null;
		ResultSet res = null;
		HeapByteBuffer _buf = HeapByteBuffer.newInstance();
		_buf.clear();
		try {
			if (!DBConnetionStateManager.getDbState())
				return;
			con = ConnectionProxy.newInstance();
			if (con != null)
				ptmt = con.prepareStatement(LOAD_SKILLS);
			if (!DbUtil.isDbNormal(DBConnetionStateManager.getDbState(), con, ptmt))
				return;
			// Retrieve all skills of this L2PcInstance from the database

			ptmt.setLong(1, player.getObjectId());
			res = ptmt.executeQuery();
			if (res == null)
				return;
			// Go though the recordset of this SQL query
			while (res.next()) {
				byte[] bytes = res.getBytes("details");
				_buf.write(bytes, 0, bytes.length);
				_buf.flip();
				int id;
				int level;
				int size = _buf.readByte();
				for (int i = 0; i < size; i++) {
					id = _buf.readInt();
					level = _buf.readInt();
					// Create a L2Skill object for each record
					L2Skill skill = SkillTable.getInstance().getInfo(id, level);
					// Add the L2Skill object to the L2Character _skills and its
					// Func objects to the calculator set of the L2Character
					player.addSkill(skill);
				}
			}
		} catch (Exception e) {
			_log.error("Error PlayerSkills restoreSkill", Config.SERVER_ID, e);
			e.printStackTrace();
		} finally {
			HeapByteBuffer.recycle(_buf);
			if (res != null)
				try {
					res.close();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			if (ptmt != null)
				ptmt.close();
			if (con != null)
				con.close();
		}
	}
}
